
package com.idealighter.game.games.fqzs.manager;

import com.alibaba.fastjson.JSONObject;

import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import com.idealighter.game.common.CommonMsgMgr;
import com.idealighter.game.core.assertions.HuohuaAssert;
import com.idealighter.game.core.assertions.IdeaAssert;
import com.idealighter.game.core.common.Game;
import com.idealighter.game.core.constant.notice.NoticeType;
import com.idealighter.game.core.error.ErrorCode;
import com.idealighter.game.dblog.service.DbLogService;
import com.idealighter.game.dictionary.dic.GameNoticeDic;
import com.idealighter.game.dictionary.dic.SharkIconPlanDic;
import com.idealighter.game.dictionary.dic.SharkIconRateDic;
import com.idealighter.game.dictionary.dic.SharkRoomDic;
import com.idealighter.game.dictionary.dic.SharkTimeDic;
import com.idealighter.game.dictionary.domain.SharkIconPlanDomain;
import com.idealighter.game.dictionary.domain.SharkIconRateDomain;
import com.idealighter.game.dictionary.domain.SharkRoomDomain;
import com.idealighter.game.dictionary.domwrapper.GameNoticeDomainWrapper;
import com.idealighter.game.event.manager.EventMgr;
import com.idealighter.game.event.struct.CancelPlayerExitEvent;
import com.idealighter.game.event.struct.ClearGameDataEvent;
import com.idealighter.game.event.struct.GameClearExitEvent;
import com.idealighter.game.event.struct.PlayerExitEvent;
import com.idealighter.game.event.struct.ShutdownGameEvent;
import com.idealighter.game.event.struct.ShutdownRoomEvent;
import com.idealighter.game.event.struct.StartGameEvent;
import com.idealighter.game.event.struct.StartRoomEvent;
import com.idealighter.game.gamehall.event.EnterRoomEvent;
import com.idealighter.game.gamehall.event.EnterTableEvent;
import com.idealighter.game.gamehall.event.ExitRoomEvent;
import com.idealighter.game.gamehall.event.ExitTableEvent;
import com.idealighter.game.gamehall.event.RoomMemInfoUpdateEvent;
import com.idealighter.game.games.common.AbstractRoom;
import com.idealighter.game.games.common.InstanceState;
import com.idealighter.game.games.common.SeatState;
import com.idealighter.game.games.fqzs.convert.SharkGameCurInfoConvert;
import com.idealighter.game.games.fqzs.message.ResGameInfoMsg;
import com.idealighter.game.games.fqzs.struct.SharkRoom;
import com.idealighter.game.games.fqzs.struct.SharkSeat;
import com.idealighter.game.games.fqzs.struct.SharkTable;
import com.idealighter.game.games.fqzs.struct.SharkTableStatus;
import com.idealighter.game.log.core.LogReason;
import com.idealighter.game.log.struct.game.Game10WinLoseLog;
import com.idealighter.game.log.struct.game.GameAfeeLog;
import com.idealighter.game.login.manager.LoginMgr;
import com.idealighter.game.message.core.ResMessage;
import com.idealighter.game.notice.manager.NoticeMsgMgr;
import com.idealighter.game.player.manager.PlayerMgr;
import com.idealighter.game.player.service.bo.PlayerBo;
import com.idealighter.game.player.struct.Player;
import com.idealighter.game.schedule.manager.ScheduleMgr;
import com.idealighter.game.server.ExecutorMgr;
import com.idealighter.game.server.core.executor.DisruptorExecutor;
import com.idealighter.utils.check.EmptyUtil;
import com.idealighter.utils.code.RandCodeUtil;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 飞禽走兽 .
 * 
 * @date 2016年2月17日 上午11:53:46
 *
 */
@Slf4j
@Singleton
public class SharkMgr {
  private static final Logger LOG = LoggerFactory.getLogger(SharkMgr.class);

  // 走兽集合
  public static final List<Integer> ZOU_SHOUS = Arrays.asList(1, 2, 3, 4);
  // 飞禽集合
  public static final List<Integer> FEI_QINGS = Arrays.asList(5, 6, 7, 8);
  // 鲨鱼集合
  public static final List<Integer> SHARKS = Arrays.asList(9, 10);
  // 银沙图标id
  public static final int YIN_SHA = 12;
  // 金沙图标id
  public static final int JIN_SHA = 13;
  // 通赔图标id
  public static final int TONG_PEI = 14;
  // 通吃图标id
  public static final int TONG_CHI = 15;

  // 飞禽图标id
  public static final int FEI_QIN = 9;
  // 走兽图标id
  public static final int ZOU_SHOU = 10;
  // 鲨鱼下注图标id
  public static final int SHARK = 11;

  // 飞禽倍率
  public static final int FEI_QIN_RATE = 2;
  // 走兽倍率
  public static final int ZOU_SHOU_RATE = 2;
  // 庄家最小筹码
  public static final int BANKER_MIN_CHIPS = 100000000;
  // 玩家单个图标最大下注筹码
  public static final int PLAYER_MAX_BET_CHIPS = 10000000;

  private final EventMgr eventMgr;
  private final ScheduleMgr scheduleMgr;
  private final SharkMsgMgr msgMgr;


  private final CommonMsgMgr commonMsgMgr;

  private final SharkDataMgr dataMgr;

  private final PlayerMgr playerMgr;
  private final SharkTimeDic timeDic;
  private final LoginMgr loginMgr;
  private final SharkIconPlanDic iconPlanDic;
  private final SharkIconRateDic iconRateDic;
  @Inject
  private GameNoticeDic gameNoticeDic;
  @Inject
  private NoticeMsgMgr noticeMsgMgr;

  private SharkControlScript sharkControl;

  private Vector<Long> logoutPlayers = new Vector<>();
  // 游戏线程executor,可以执行普通任务和定时任务
  public final DisruptorExecutor gameExecutor;

  /**
   * 构造函数 .
   * 
   * @param eventMgr .
   * @param executorMgr .
   * @param scheduleMgr .
   * @param msgMgr .
   * @param dataMgr .
   * @param roomDic .
   * @param playerMgr .
   * @param timeDic .
   * @param loginMgr .
   * @param iconPlanDic .
   * @param iconRateDic .
   */
  @Inject
  public SharkMgr(EventMgr eventMgr, ExecutorMgr executorMgr, ScheduleMgr scheduleMgr,
      SharkMsgMgr msgMgr, SharkDataMgr dataMgr, SharkRoomDic roomDic, PlayerMgr playerMgr,
      SharkTimeDic timeDic, LoginMgr loginMgr, SharkIconPlanDic iconPlanDic,
      SharkIconRateDic iconRateDic, CommonMsgMgr commonMsgMgr) {
    this.eventMgr = eventMgr;
    this.scheduleMgr = scheduleMgr;
    this.msgMgr = msgMgr;
    this.dataMgr = dataMgr;
    this.playerMgr = playerMgr;
    this.timeDic = timeDic;
    this.loginMgr = loginMgr;
    this.iconPlanDic = iconPlanDic;
    this.iconRateDic = iconRateDic;
    this.commonMsgMgr = commonMsgMgr;

    eventMgr.register(this);
    this.gameExecutor = executorMgr.getGameExecutor(Game.FQZS.getModuleId());
  }


  /**
   * 清空离线玩家.
   */
  private void clearLogoutPlayer() {
    if (logoutPlayers != null && !logoutPlayers.isEmpty()) {
      logoutPlayers.forEach(playerId -> {
        log.info("[飞禽走兽]清空玩家 {}", playerId);

        SharkSeat seat = dataMgr.getPlayerSeat(playerId);
        if (seat != null) {
          doExitTabl(seat);
        }
        Player player = playerMgr.getPlayer(playerId);
        // 玩家是否在房间中
        if (player != null && player.curRoom instanceof SharkRoom) {
          // 退出房间
          exitRoom(player);

          GameClearExitEvent gameClearExitEvent =
              new GameClearExitEvent(player, Game.FQZS.getDesc());
          eventMgr.post(gameClearExitEvent);
        }

      });
      logoutPlayers.clear();
    }

  }

  private void kickoutForRoomClose(Player player) {
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat != null) {
      doExitTabl(seat);
    }
    if (player.curRoom != null) {
      exitRoom(player);
      commonMsgMgr.noticeRoomCloseKickout(player);
    }

  }

  /**
   * 玩家退出游戏.
   * 
   * @param event .
   */
  @Subscribe
  public void onPlayerExitGame(PlayerExitEvent event) {
    gameExecutor.execute(() -> {
      Player player = event.player;
      if (player.curRoom instanceof SharkRoom) {
        long playerId = player.getId();
        if (!logoutPlayers.contains(playerId)) {
          logoutPlayers.add(playerId);
          log.info("[飞禽走兽]玩家预约退出 {}", playerId);
        }
      }
    });
  }

  /**
   * 取消玩家退出游戏.
   * 
   * @param event 退出游戏事件.
   */
  @Subscribe
  public void onCancelPlayerExitEvent(CancelPlayerExitEvent event) {
    gameExecutor.execute(() -> {

      Player player = event.player;
      long playerId = player.getId();
      if (logoutPlayers.contains(playerId)) {
        logoutPlayers.remove(playerId);
        log.info("[飞禽走兽]玩家取消退出 {}", playerId);
      }
    });
  }


  /**
   * 检验是否房间关闭，如果桌子不在游戏中或者游戏正在休息，顺便把桌子关闭 .
   *
   * @author abin
   * @date 2018年4月28日 下午3:41:20
   * @param table 桌子.
   */
  private boolean checkClose(SharkTable table) {
    boolean remove = false;
    if (table.getRoom().getInstanceState() == InstanceState.TO_REMOVE) {
      if (table.getStatus().equals(SharkTableStatus.REST)) {
        for (Long playerId : table.players()) {
          Player player = playerMgr.getPlayer(playerId);
          if (player != null) {
            kickoutForRoomClose(player);
          }
        }
        SharkRoom room = table.getRoom();
        room.removeTable(table.getId().intValue());
        if (table != null && table.stepFuture != null) {
          table.stepFuture.cancel(false);
        }

        table = null;
        remove = true;
      }
    }
    return remove;
  }

  private void deleteRooms(List<SharkRoom> deleteRooms) {
    if (EmptyUtil.listIsNotEmpty(deleteRooms)) {
      for (Iterator<SharkRoom> iterator = deleteRooms.iterator(); iterator.hasNext();) {
        SharkRoom sharkRoom = iterator.next();
        List<SharkTable> tables = new ArrayList<>(sharkRoom.tables());
        for (Iterator<SharkTable> tableIterator = tables.iterator(); tableIterator.hasNext();) {
          SharkTable sharkTable = tableIterator.next();
          checkClose(sharkTable);
        }
        dataMgr.checkRemoveInstance(sharkRoom.getId().intValue(), sharkRoom.getInstanceId());
      }
    }
  }

  /**
   * 玩家开始游戏.
   * 
   * @param event 事件.
   */
  @Subscribe
  public void onStartRoom(StartRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.FQZS) {

        List<SharkRoom> deleteRooms = dataMgr.startRoom(event.getRoomId(), true);
        deleteRooms(deleteRooms);

        SharkRoom sharkRoom = dataMgr.getNewestRoom(event.getRoomId());
        if (sharkRoom != null) {
          for (SharkTable table : sharkRoom.tables()) {
            tableRest(table);
          }
        }
      }
    });
  }

  /**
   * 关闭事件.
   * 
   * @param event 关闭房间事件.
   */
  @Subscribe
  public void onShutDownRoom(ShutdownRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.FQZS) {
        List<SharkRoom> deleteRooms = dataMgr.deleteRoom(event.getRoomId());
        deleteRooms(deleteRooms);
      }
    });
  }

  /**
   * 开启游戏事件.
   * 
   * @param event 事件.
   */
  @Subscribe
  public void onStartGame(StartGameEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.FQZS) {
        List<Integer> ids = dataMgr.reloadRoom();
        if (EmptyUtil.listIsNotEmpty(ids)) {
          for (Integer id : ids) {
            List<SharkRoom> deleteRooms = dataMgr.startRoom(id, false);
            deleteRooms(deleteRooms);

            // 启动table
            SharkRoom room = dataMgr.getNewestRoom(id);
            if (room != null) {
              for (SharkTable table : room.tables()) {
                tableRest(table);
              }
            }
          }
        }
      }
    });
  }

  /**
   * 关闭游戏事件.
   * 
   * @param event 事件.
   */
  @Subscribe
  public void onShutdownGame(ShutdownGameEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.FQZS) {
        Collection<SharkRoom> rooms = dataMgr.allRooms();
        if (!EmptyUtil.isEmpty(rooms)) {
          for (SharkRoom room : rooms) {
            List<SharkRoom> deleteRooms = dataMgr.deleteRoom(room.getId().intValue());
            deleteRooms(deleteRooms);
          }
        }

      }
    });
  }

  @Subscribe
  public void onClearGameData(ClearGameDataEvent event) {
    gameExecutor.execute(() -> doClearGameData(event.playerId));
  }

  /**
   * 开始飞禽走兽游戏.
   */
  @Inject
  private void startGame(SharkControlScript sharkControl) {
    this.sharkControl = sharkControl;
    for (SharkRoom room : dataMgr.newestRooms()) {
      for (SharkTable table : room.tables()) {
        tableRest(table);
      }
    }
  }

  /**
   * 桌子休息 .
   * 
   * @param table .
   */
  private void tableRest(SharkTable table) {
    /*
     * 重置桌子和位置(包括系统位置)的数据 .
     */
    table.setRewardIcon(0);
    table.setIconPlan(0);
    table.getIconRates().clear();
    table.getTableBets().clear();

    // 休息阶段
    table.setStatus(SharkTableStatus.REST);
    for (SharkSeat seat : table.getSeats()) {
      seat.resetGameData();
    }
    if (checkClose(table)) {
      dataMgr.checkRemoveInstance(table.getRoom().getId().intValue(),
          table.getRoom().getInstanceId());
      return;
    }

    // 随机图标倍率
    randomRewardRates(table);
    // 休息时间
    int restTime = timeDic.get(GameState.REST.getStage()).getTime();
    // 改变庄家
    doChangeBanker(table);
    // 发送休息阶段消息
    msgMgr.sendRestStageMsg(table, restTime, null);

    table.stepFuture =
        scheduleMgr.schedule(() -> tableBet(table), restTime, TimeUnit.SECONDS, gameExecutor);
    LOG.info("[飞禽走兽]房间[{}]桌子[{}]阶段[{}]", table.getRoom().getId(), table.getId(),
        table.getStatus().desc);
  }


  /**
   * 随机图标倍率 .
   * 
   * @param table .
   */
  private void randomRewardRates(SharkTable table) {
    /*
     * 随机确定图标方案 .
     */
    // 图标方案
    int iconPlan = 1;
    long randomPr = RandCodeUtil.random(iconPlanDic.getTotalProbability());
    long sum = 0;
    for (SharkIconPlanDomain iconPlanDom : iconPlanDic.list()) {
      sum += iconPlanDom.getProbability();
      if (randomPr < sum) {
        iconPlan = iconPlanDom.getId();
        break;
      }
    }
    table.setIconPlan(iconPlan);

    /*
     * 随机确定指定图标方案的图标倍率,其实只有金鲨的倍率是变化的 .
     */
    for (SharkIconRateDomain iconRateDom : iconRateDic.getIconRates().row(iconPlan).values()) {
      table.getIconRates().put(iconRateDom.getIcon(),
          RandCodeUtil.random(iconRateDom.getMinRate(), iconRateDom.getMaxRate()));
    }
  }

  /**
   * 桌子下注 .
   * 
   * @param table .
   */
  private void tableBet(SharkTable table) {
    // 下注阶段
    table.setStatus(SharkTableStatus.BET);
    // 发送下注阶段消息
    msgMgr.sendBetStageMsg(table, timeDic.get(GameState.BET.getStage()).getTime(), null);
    // 下注时间
    int betTime = timeDic.get(GameState.BET.getStage()).getTime();
    LOG.info("[飞禽走兽]房间[{}]桌子[{}]阶段[{}]", table.getRoom().getId(), table.getId(),
        table.getStatus().desc);
    table.stepFuture =
        scheduleMgr.schedule(() -> tableGaming(table), betTime, TimeUnit.SECONDS, gameExecutor);
  }

  /**
   * 桌子游戏阶段 .
   * 
   * @param table .
   */
  private void tableGaming(SharkTable table) {
    // 游戏阶段
    table.setStatus(SharkTableStatus.GAMING);
    Integer rewardIcon = sharkControl.controlRandomRewardIcon(table);

    /*
     * 随机中间图标和中奖索引,一个图标在转盘可能有有多个位置,第一次转到金鲨或银鲨会有第二次转动 .
     */
    final int iconIndex = rewardIcon; // randomRewardIconIndex(table, rewardIcon);

    table.setRewardIcon(rewardIcon);
    // 历史图标
    table.getHistoryRewardIcons().add(rewardIcon);

    // 游戏时间(转动和结算)
    int gameTime = timeDic.get(GameState.GAME.getStage()).getTime();

    /*
     * 计算结算筹码 .
     */
    SharkSeat banker = table.getBanker();
    for (SharkSeat seat : table.getSeats()) {
      if (seat.getPlayerId() != banker.getPlayerId() && seat.getPlayerId() > 0) {
        // 中奖筹码
        long iconRewardChips = calIconRewardChips(rewardIcon, table.getIconRates(), seat.getBets());

        banker.setBillChips(banker.getBillChips() + seat.totalBets() - iconRewardChips);
        seat.setBillChips(seat.getBillChips() + iconRewardChips);

        if (seat.getBillChips() > 0) {
          SharkTable sharkTable = seat.getTable();
          sharkTable.addTopTopReward(seat.getPlayerId(), seat.getPlayerName(), seat.getBillChips());
        }
      }
    }

    table.stepFuture =
        scheduleMgr.schedule(() -> tableBill(table), gameTime, TimeUnit.SECONDS, gameExecutor);
    // 发送游戏阶段消息
    msgMgr.sendGameStageMsg(table, iconIndex, null);
    LOG.info("[飞禽走兽]房间[{}]桌子[{}]阶段[{}],开奖图标[{}]", table.getRoom().getId(), table.getId(),
        table.getStatus().desc, table.getRewardIcon());
  }

  /**
   * 如果满足条件发送公告 .
   * 
   * @param seat 座位信息.
   * @param firstIcon .
   * @param chips .
   */
  private void sendedNotice(SharkSeat seat, int firstIcon, long chips) {
    for (GameNoticeDomainWrapper dom : gameNoticeDic.getGameDoms().get(Game.FQZS.getType())) {
      JSONObject conditionData = dom.conditionData();
      // 公告条件水浒类型
      Long minChips = conditionData.getLong("minChips");
      if ((firstIcon == JIN_SHA || firstIcon == YIN_SHA) && chips >= minChips) {
        String sendContent = MessageFormat.format(dom.getContent(), seat.getPlayerName(), chips);
        noticeMsgMgr.sendMarqueeNoticeMsg(sendContent, NoticeType.GAME, dom.getInterval(),
            dom.getTimes(), dom.getColor());
        // chatMsgMgr.sendSysNoticeMsg(sendContent);
      }
    }
  }

  /**
   * 计算总的输赢 .
   * 
   * @param icon 中奖图标.
   * @param iconRates 图标中奖赔率.
   * @param iconBetsMap 下注情况.
   * @return 中奖筹码
   */
  public long calIconRewardChips(int icon, Map<Integer, Integer> iconRates,
      Map<Integer, Long> iconBetsMap) {
    long reward = 0L;
    // 通吃
    if (icon == TONG_CHI) {
      reward = 0L;
    } else if (icon == TONG_PEI) { // 通赔
      for (Integer key : iconBetsMap.keySet()) {
        Long bets = iconBetsMap.get(key);
        if (key == FEI_QIN) {
          reward += FEI_QIN_RATE * bets;
        } else if (key == ZOU_SHOU) {
          reward += ZOU_SHOU_RATE * bets;
        } else if (key == SHARK) {
          reward += iconRates.get(YIN_SHA) * bets;
        } else {
          reward += iconRates.get(key) * bets;
        }
      }
    } else if (icon == JIN_SHA) {
      long sharkBets = iconBetsMap.getOrDefault(SHARK, 0L);
      reward += iconRates.get(JIN_SHA) * sharkBets;
    } else if (icon == YIN_SHA) {
      long sharkBets = iconBetsMap.getOrDefault(SHARK, 0L);
      reward += iconRates.get(YIN_SHA) * sharkBets;
    } else {
      reward += iconRates.get(icon) * iconBetsMap.getOrDefault(icon, 0L);
      if (FEI_QINGS.contains(icon)) { // 飞禽
        long feiQinBet = iconBetsMap.getOrDefault(FEI_QIN, 0L);
        reward += FEI_QIN_RATE * feiQinBet;
      } else if (ZOU_SHOUS.contains(icon)) { // 走兽
        long zouShouBet = iconBetsMap.getOrDefault(ZOU_SHOU, 0L);
        reward += ZOU_SHOU_RATE * zouShouBet;
      }
    }
    return reward;
  }

  /**
   * 牌桌结算(普通座位和系统座位) .
   * 
   * @param table .
   */
  private void tableBill(SharkTable table) {
    SharkSeat banker = table.getBanker();
    banker.setBankerNum(banker.getBankerNum() + 1);
    banker.setBankerScore(banker.getBankerScore() + banker.getBillChips());
    banker.setTotalChips(banker.getTotalChips() + banker.getBillChips());

    for (SharkSeat seat : table.getSeats()) {
      if (seat.getPlayerId() > 0) {
        doBill(seat);
      }
    }

    table.getSysSeat().resetGameData();
    SharkRoomDomain roomDomain = table.getRoom().getRoomDomain();
    // 结算后，如果庄家是玩家，校验钱够不够
    if (banker.getTotalChips() < roomDomain.getOffBankerChips()) {
      table.randSysBankerInfo();
      table.setBanker(table.getSysSeat());
      doChangeBanker(table);
    }

    // 控制坐庄次数
    if (banker.getBankerNum() >= 10) {
      table.randSysBankerInfo();
      table.setBanker(table.getSysSeat());
      doChangeBanker(table);
    }
    // if (banker.playerId > 0 && banker.totalChips < roomDomain.getOffBankerChips()) {
    // table.banker = table.sysSeat;
    // doChangeBanker(table);
    // }

    // 同步庄家信息
    msgMgr.sendBankerInfoMsg(table);

    // 清空预约退出玩家.
    clearLogoutPlayer();
    // 结算完后休息
    tableRest(table);
    LOG.info("[飞禽走兽]房间[{}]桌子[{}]阶段[{}]", table.getRoom().getId(), table.getId(),
        table.getStatus().desc);
  }

  /**
   * 座位结算 .
   * 
   * @param seat 座位信息.
   */
  private void doBill(SharkSeat seat) {
    SharkTable table = seat.getTable();
    long playerId = seat.getPlayerId();
    SharkRoomDomain roomDomain = seat.getTable().getRoom().getRoomDomain();
    // 扣除输赢
    seat.setTotalChips(seat.getTotalChips() + seat.getBillChips());
    long billGold = convertGold(roomDomain, seat.getBillChips());

    // 是否发送奖励公告
    sendedNotice(seat, table.getRewardIcon(), seat.getBillChips());

    /*
     * 游戏结算金币 .
     */
    Player player = playerMgr.getPlayer(playerId);
    PlayerBo playerDom = playerMgr.selectPlayer(playerId);
    if (player != null) {
      playerMgr.addGold(player, billGold, LogReason.SHARK_BILL);
      // 新增赢分 计算
      if (billGold > 0) {
        playerMgr.addWinGold(player, seat.getBillChips());
      }
    } else {
      playerMgr.addGold(playerDom, billGold, LogReason.SHARK_BILL);
    }

    // 最终结果输赢筹码
    long resChips = seat.getBillChips() - seat.totalBets();
    long resGold = convertGold(roomDomain, resChips);

    // 数据库输赢日志
    DbLogService
        .log(new Game10WinLoseLog(playerDom, seat, resChips, resGold, LogReason.SHARK_BILL));
    seat.saveInPreBets();
    seat.resetGameData();
    seat.getTable().clearTopReward();
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(playerMgr.getPlayer(playerId)));
  }

  /**
   * 发送游戏阶段信息 .
   * 
   * @param player 玩家.
   */
  public ResMessage sendGameStageInfo(Player player) {
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[飞禽走兽]玩家[{}][{}]还未进入桌子不能获取游戏阶段信息", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }
    SharkTable table = seat.getTable();
    // 游戏阶段剩余时间
    int leftTime = (int) table.stepFuture.getDelay(TimeUnit.SECONDS);

    switch (table.getStatus()) {
      case BET:
        msgMgr.sendBetStageMsg(table, leftTime, seat);
        msgMgr.sendTableBetInfosMsg(table);
        break;
      case GAMING:
        msgMgr.sendGameStageMsg(table, 0, seat);
        break;
      case REST:
        msgMgr.sendRestStageMsg(table, leftTime, seat);
        break;
      default:
        break;
    }
    return ResMessage.DEFAULT;
  }

  /**
   * 进入游戏大厅 .
   * 
   * @param player 玩家.
   */
  public ResMessage enterGameHall(Player player) {
    if (player.curRoom != null) {
      Game game = player.curRoom.game();
      LOG.warn("[飞禽走兽]玩家[{}][{}]在[{}]游戏中，暂时无法进入游戏", player.getId(), player.getPlayerName(),
          game.getDesc());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ALREADY_IN);
    }

    LOG.info("[飞禽走兽]玩家[{}][{}]进入大厅成功", player.getPlayerName(), player.getId());
    return msgMgr.sendEnterGameHallMsg();
  }

  /**
   * 玩家进入房间 .
   * 
   * @param player 玩家.
   * @param roomId .
   */
  public ResMessage enterRoom(Player player, int roomId) {
    SharkRoom room = dataMgr.getNewestRoom(roomId);


    HuohuaAssert.isTrue(room != null && room.getInstanceState().equals(InstanceState.NORMAL),
        ErrorCode.GAME_ROOM_CLOSED);

    AbstractRoom curRoom = player.curRoom;
    if (curRoom != null && !curRoom.equals(room)) {
      LOG.warn("[飞禽走兽]玩家[{}][{}]当前在游戏[{}]房间[{}]中不能进入房间", player.getId(), player.getPlayerName(),
          curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ALREADY_IN);
    } else if (curRoom == null) {
      SharkRoomDomain roomDom = room.getRoomDomain();
      if (roomDom.getIsActive() != 1) {
        LOG.error("[飞禽走兽]玩家[{}][{}]进入的房间[{}]已经关闭", player.getPlayerName(), player.getId(), roomId);
        HuohuaAssert.isTrue(false);
      }
      if (player.getGold() < roomDom.getLower()) {
        LOG.warn("[飞禽走兽]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(),
            roomId, roomDom.getLower());
        HuohuaAssert.isTrue(false, ErrorCode.PLAYER_GOLD_NOT_ENOUGH);
      }
      long upper = roomDom.getUpper();
      if (upper > 0L && player.getGold() > upper) {
        LOG.warn("[飞禽走兽]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(),
            roomId, roomDom.getUpper());
        HuohuaAssert.isTrue(false, ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD);
      }
      if (!player.vip()
          && room.getPlayers().size() > roomDom.getMaxNum() * roomDom.getOrdinarPeople() / 100) {
        LOG.warn("[飞禽走兽]玩家[{}][{}]进入的房间[{}]普通玩家人数已超过上限", player.getPlayerName(), player.getId(),
            roomId, roomDom.getMaxNum());
        HuohuaAssert.isTrue(false, ErrorCode.GAME_NOT_VIP_ROOM_FULL);
      }

      if (room.getPlayers().size() >= roomDom.getMaxNum()) {
        LOG.warn("[飞禽走兽]玩家[{}][{}]进入的房间[{}]已超过最大人数[{}]", player.getPlayerName(), player.getId(),
            roomId, roomDom.getMaxNum());
        HuohuaAssert.isTrue(false, ErrorCode.GAME_ROOM_FULL);
      }

      // 修改玩家当前房间
      player.curRoom = room;
      room.getPlayers().add(player.getId());

      LOG.info("[飞禽走兽]玩家[{}][{}]进入房间[{}]成功", player.getPlayerName(), player.getId(), roomId);
      // 触发进入房间事件
      eventMgr.post(new EnterRoomEvent(player, room));
    }
    // 发送进入房间消息
    return msgMgr.sendEnterRoomMsg(player);
  }

  /**
   * 玩家金币兑换筹码 .
   * 
   * @param player 玩家.
   * @param gold . 兑换筹码的金币
   */
  public ResMessage exchangeChips(Player player, long gold) {
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[飞禽走兽]玩家[{}][{}]不在牌桌中不能兑换筹码", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    if (player.getGold() < gold
        + convertGold(seat.getTable().getRoom().getRoomDomain(), seat.getTotalChips())) {
      LOG.error("[飞禽走兽]玩家[{}][{}]兑换的金币[{}]加上筹码[{}]兑换的金币超过自己的金币总和[{}]", player.getPlayerName(),
          player.getId(), gold, seat.getTotalChips(), player.getGold());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_GOLD_NOT_ENOUGH);
    }

    SharkTable table = seat.getTable();
    if (table.getBanker() == seat) { // 庄家全程不让上下分
      LOG.warn("[飞禽走兽]玩家[{}][{}]是庄家不允许上下分", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_BANKER_NOT_ALLOW_EXCHANGE);
    } else { // 闲家只有在下注和休息阶段可以上下分
      if (table.getStatus() == SharkTableStatus.GAMING) {
        LOG.warn("[飞禽走兽]玩家[{}][{}]游戏阶段不能上下分", player.getPlayerName(), player.getId());
        HuohuaAssert.isTrue(false, ErrorCode.GAME_NOT_ALLOW_EXCHANGE);
      }
    }

    long chips = convertChip(seat.getTable().getRoom().getRoomDomain(), gold);
    seat.setTotalChips(seat.getTotalChips() + chips);
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);

    LOG.info("[飞禽走兽]玩家[{}][{}]用金币[{}]兑换筹码[{}]成功", player.getPlayerName(), player.getId(), gold,
        chips);

    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));

    return ResMessage.DEFAULT;
  }

  /**
   * 玩家筹码兑换金币 .
   * 
   * @param player 玩家.
   * @param chips .
   */
  public ResMessage exchangeGold(Player player, long chips) {
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[飞禽走兽]玩家[{}][{}]不在牌桌中不能兑换金币", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    if (seat.getTotalChips() < chips) {
      LOG.error("[飞禽走兽]玩家[{}][{}]兑换的筹码[{}]超过自己的筹码总和[{}]", player.getPlayerName(), player.getId(),
          chips, seat.getTotalChips());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_CHIP_NOT_ENOUGH);
    }

    SharkTable table = seat.getTable();
    if (table.getBanker() == seat) { // 庄家全程不让上下分
      LOG.warn("[飞禽走兽]玩家[{}][{}]是庄家不允许上下分", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_BANKER_NOT_ALLOW_EXCHANGE);
    } else { // 闲家只有在下注和休息阶段可以上下分
      if (table.getStatus() == SharkTableStatus.GAMING) {
        LOG.warn("[飞禽走兽]玩家[{}][{}]游戏阶段不能上下分", player.getPlayerName(), player.getId());
        HuohuaAssert.isTrue(false, ErrorCode.GAME_NOT_ALLOW_EXCHANGE);
      }
    }

    seat.setTotalChips(seat.getTotalChips() - chips);
    long gold = convertGold(seat.getTable().getRoom().getRoomDomain(), chips);

    // 发送兑换筹码变更
    msgMgr.sendChipsChangeMsg(seat);

    LOG.info("[飞禽走兽]玩家[{}][{}]用筹码[{}]兑换金币[{}]成功", player.getPlayerName(), player.getId(), chips,
        gold);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));

    return ResMessage.DEFAULT;
  }

  /**
   * 筹码换算金币,只会是多个金币等于一个筹码 .
   * 
   * @param chips .
   */
  public long convertGold(SharkRoomDomain roomDom, long chips) {
    return chips * roomDom.getProportionGold() / roomDom.getProportionChips();
  }

  /**
   * 金币换算筹码 .
   * 
   * @param gold .
   * @return
   */
  public long convertChip(SharkRoomDomain roomDom, long gold) {
    return gold * roomDom.getProportionChips() / roomDom.getProportionGold();
  }

  /**
   * 玩家退出房间 .
   * 
   * @param player 玩家.
   */
  public ResMessage exitRoom(Player player) {
    AbstractRoom curRoom = player.curRoom;
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());

    if (curRoom instanceof SharkRoom) {
      if (seat == null) {
        player.curRoom = null;
        SharkRoom room = (SharkRoom) curRoom;
        room.getPlayers().remove(player.getId());

        LOG.info("[飞禽走兽]玩家[{}][{}]退出房间[{}]", player.getPlayerName(), player.getId(), room.getId());
        // 触发退出房间事件
        eventMgr.post(new ExitRoomEvent(player, curRoom));

        // 清空当前的房间
        player.curRoom = null;

        return msgMgr.sendExitRoomMsg(0);
      } else {
        LOG.error("[飞禽走兽]玩家[{}][{}]在座位中不能退出房间,请先退出桌子", player.getPlayerName(), player.getId());

        return msgMgr.sendExitRoomMsg(1);
      }
    } else {
      LOG.warn("[飞禽走兽]玩家[{}][{}]不在房间中，无法退出房间", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
      return null;
    }
  }

  /**
   * 查找空位置,优先查找有人的桌子 .
   * 
   * @param player 玩家.
   * @param room .
   * @return
   */
  private SharkSeat findEmptySeat(Player player, SharkRoom room) {
    SharkSeat emptySeat = null;
    ArrayList<SharkTable> tables = new ArrayList<>(room.getTables().values());
    Collections.shuffle(tables);

    for (SharkTable table : tables) {
      if (table.getLimitIp() != null && table.getLimitIp().equals(player.ip)) {
        continue;
      }

      if (player.getGold() < table.getLimitGold()) {
        continue;
      }

      boolean hasPlayer = false;
      SharkSeat tableEmptySeat = null;

      for (SharkSeat seat : table.getSeats()) {
        if (seat.getPlayerId() == 0) {
          emptySeat = seat;
          tableEmptySeat = seat;
        } else {
          hasPlayer = true;
        }
      }

      if (hasPlayer && tableEmptySeat != null) {
        return tableEmptySeat;
      }
    }

    return emptySeat;
  }

  /**
   * 申请庄家 .
   * 
   * @param player 玩家.
   */
  public ResMessage applyBanker(Player player) {
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[飞禽走兽]玩家[{}][{}]没有在桌子中不能申请庄家", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }
    SharkRoomDomain sharkRoomDomain = seat.getTable().getRoom().getRoomDomain();
    if (seat.getTotalChips() < sharkRoomDomain.getBeBankerChips()) {
      LOG.error("[飞禽走兽]玩家[{}][{}]不够庄家最小筹码数[{}]申请失败", player.getPlayerName(), player.getId(),
          BANKER_MIN_CHIPS);
      HuohuaAssert.isTrue(false, ErrorCode.GAME_CHIP_NOT_ENOUGH, BANKER_MIN_CHIPS + "");
    }

    SharkTable table = seat.getTable();
    if (table.getApplyBankers().size() > sharkRoomDomain.getBankerApplyNums()) {
      LOG.warn("[飞禽走兽]玩家[{}][{}]申请庄家时人数超过上限[{}]", player.getPlayerName(), player.getId(),
          sharkRoomDomain.getBankerApplyNums());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_BANKER_QUEUE_FULL);
    } else if (table.getApplyBankers().contains(player.getId())) {
      LOG.warn("[飞禽走兽]玩家[{}][{}]重复申请庄家", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_BANKER_QUEUE_FULL);
    }
    table.getApplyBankers().add(player.getId());
    if (table.getStatus() == SharkTableStatus.REST) {
      doChangeBanker(table);
    } else {
      LOG.info("[飞禽走兽]玩家[{}][{}]申请庄家成功", player.getPlayerName(), player.getId());
      msgMgr.sendApplyBankersMsg(table);
    }

    return ResMessage.DEFAULT;
  }

  /**
   * 玩家下庄 .
   * 
   * @param player 玩家.
   */
  public ResMessage offBanker(Player player) {
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[飞禽走兽]玩家[{}][{}]没有在桌子中不能下庄", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }
    SharkSeat banker = seat.getTable().getBanker();
    if (banker != seat) {
      LOG.error("[飞禽走兽]玩家[{}][{}]不是庄家不能下庄", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    SharkTable table = seat.getTable();
    if (table.getStatus() != SharkTableStatus.REST) {
      LOG.error("[飞禽走兽]玩家[{}][{}]只有在休息阶段才能下庄", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    banker.setBankerNum(0);
    banker.setBankerScore(0);
    table.randSysBankerInfo();
    table.setBanker(table.getSysSeat());
    msgMgr.sendBankerInfoMsg(table);
    doChangeBanker(table);

    LOG.info("[飞禽走兽]玩家[{}][{}]下庄成功", player.getPlayerName(), player.getId());

    return ResMessage.DEFAULT;
  }

  /**
   * 取消申请庄家 .
   * 
   * @param player 玩家.
   */
  public ResMessage cancelApplyBanker(Player player) {
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[飞禽走兽]玩家[{}][{}]没有在桌子中不能申请庄家", player.getPlayerName(), player.getId());
      IdeaAssert.isTrue(false);
    }

    SharkTable table = seat.getTable();
    if (table.getApplyBankers().contains(player.getId())) {
      table.getApplyBankers().remove(player.getId());
      msgMgr.sendApplyBankersMsg(table);
    }

    LOG.info("[飞禽走兽]玩家[{}][{}]取消申请庄家成功", player.getPlayerName(), player.getId());

    return ResMessage.DEFAULT;
  }

  /**
   * 如可以的话，改变庄家 .
   * 
   * @param table .
   */
  private void doChangeBanker(SharkTable table) {
    if (table.getBanker() == table.getSysSeat()) {
      Long applicant = null;
      while ((applicant = table.getApplyBankers().poll()) != null) {
        SharkSeat seat = dataMgr.getPlayerSeat(applicant);
        if (seat.getTotalChips() >= BANKER_MIN_CHIPS) {
          table.setBanker(seat);
          // 换庄后重置系统位置数据
          table.getSysSeat().reset();
          // msgMgr.sendBankerInfoMsg(table);
          break;
        }
      }
      // 临时放在此地
      msgMgr.sendBankerInfoMsg(table);
      // 便利完之后，发送剩余的申请列表
      msgMgr.sendApplyBankersMsg(table);
    }
  }

  /**
   * 清除下注 .
   * 
   * @param player 玩家.
   */
  public ResMessage clearBet(Player player) {
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[飞禽走兽]玩家[{}][{}]没有在桌子中不能清除下注筹码", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    SharkTable table = seat.getTable();
    if (table.getStatus() != SharkTableStatus.BET) {
      LOG.error("[飞禽走兽]玩家[{}][{}]不是下注阶段不能清除下注", player.getPlayerName(), player.getId());
      return msgMgr.sendClearBetMsg(1);
    }

    // 牌桌合计下注
    Map<Integer, Long> tableBets = table.getTableBets();
    // 合计下注
    final long totalBets = seat.totalBets();
    Map<Integer, Long> bets = seat.getBets();
    for (Entry<Integer, Long> betEtr : bets.entrySet()) {
      int icon = betEtr.getKey();
      long tableBet = tableBets.getOrDefault(icon, 0L);
      tableBets.put(icon, tableBet - betEtr.getValue());
    }

    bets.clear();
    seat.setTotalChips(seat.getTotalChips() + totalBets);
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);
    playerMgr.addGold(player, convertGold(seat.getTable().getRoom().getRoomDomain(), totalBets),
        LogReason.SHARK_EXIT_TABLE_RETURN_BET);
    // // 触发房间大厅成员信息变更事件
    // eventMgr.post(new RoomMemInfoUpdateEvent(player));

    msgMgr.sendTableBetInfosMsg(seat.getTable());
    LOG.info("[飞禽走兽]玩家[{}][{}]清除下注成功", player.getPlayerName(), player.getId());

    return msgMgr.sendClearBetMsg(0);
  }

  /**
   * 快速进入桌子 .
   * 
   * @param player 玩家.
   * @param roomId .
   */
  public ResMessage fastEnterTable(Player player, int roomId) {
    if (!(player.curRoom != null && player.curRoom instanceof SharkRoom)) {
      LOG.error("[飞禽走兽]玩家[{}][{}]当前不在房间中不能直接进入桌子", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    SharkRoom room = dataMgr.getNewestRoom(roomId);
    HuohuaAssert.isTrue(room != null && room.equals(player.curRoom),
        ErrorCode.INTERNAL_SERVER_ERROR);

    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      seat = findEmptySeat(player, room);
      HuohuaAssert.isTrue(seat != null, ErrorCode.GAME_NOT_SIT);

      SharkTable table = seat.getTable();

      SharkRoomDomain roomDom = room.getRoomDomain();
      if (player.getGold() < roomDom.getLower()) {
        LOG.warn("[飞禽走兽]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(),
            roomId, roomDom.getLower());
        HuohuaAssert.isTrue(false, ErrorCode.PLAYER_GOLD_NOT_ENOUGH);
      }

      long upper = roomDom.getUpper();
      if (upper > 0L && player.getGold() > upper) {
        LOG.warn("[飞禽走兽]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(),
            roomId, roomDom.getUpper());
        HuohuaAssert.isTrue(false, ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD);
      }

      if (player.getGold() < roomDom.getAfee()) {
        LOG.warn("[飞禽走兽]玩家[{}][{}]进入桌子金币不足台费", player.getPlayerName(), player.getId());
        HuohuaAssert.isTrue(false, ErrorCode.GAME_NOT_ENOUGH_FEE);
      } else {
        playerMgr.minusGold(player, roomDom.getAfee(), LogReason.SHARK_ENTER_TABLE_MINUS_AFEE);
        DbLogService.log(new GameAfeeLog(player, table, roomDom.getAfee()));
      }

      seat.setPlayerId(player.getId());
      seat.setPlayerName(player.getPlayerName());
      seat.setSex(player.getSex());
      seat.setRobot(player.robot());
      seat.setState(SeatState.GAMING);
      seat.setTotalChips(convertChip(roomDom, player.getGold()));

      // 更新玩家座位
      dataMgr.updatePlayerSeats(player.getId(), seat);
      // 触发进入桌子事件
      eventMgr.post(new EnterTableEvent(player, seat));

      msgMgr.sendApplyBankersMsg(seat);
      msgMgr.sendBankerInfoMsg(seat);
    }

    LOG.info("[飞禽走兽]玩家[{}][{}]进入桌子[{}]座位[{}]成功", player.getPlayerName(), player.getId(),
        seat.getTable().getId(), seat.getOrder());

    player.curSeat = seat;

    // 向玩家发送进入牌桌结果信息
    return msgMgr.sendFastEnterTableMsg(player, seat.getTable(), seat.getTotalChips());
  }

  /**
   * 获取游戏当前信息.
   * 
   * @Title gameCurInfo.
   * @author houdongsheng
   * @date 2018年1月15日 下午8:26:48
   * @param player 玩家
   * @return SharkGameCurInfo
   */
  public ResMessage getGameInfo(Player player) {
    SharkRoom curRoom = (SharkRoom) player.curRoom;
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    ResMessage result = null;
    if (curRoom != null && seat != null) {
      seat.setTotalChips(convertChip(curRoom.getRoomDomain(), player.getGold()));
      result = SharkGameCurInfoConvert.INSTANCE.toSharkGameCurInfo(seat);
      if (logoutPlayers.contains(player.getId())) {
        logoutPlayers.remove(player.getId());
        log.info("[飞禽走兽]玩家取消退出 {}", player.getId());
      }
    } else {
      result = new ResGameInfoMsg();
    }
    return result;
  }

  /**
   * 玩家退出牌桌 .
   * 
   * @param player 玩家.
   */
  public ResMessage exitTable(Player player) {
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[飞禽走兽]玩家[{}][{}]没在牌桌中不能退出牌桌", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    // 退出桌子
    doExitTabl(seat);
    return ResMessage.DEFAULT;
  }

  /**
   * 退出桌子 . 1.玩家正常退出牌桌,能在在线玩家中拿到player,正常退出桌子 2.玩家非正常退出(如掉线)后,从活跃玩家或数据库中拿到PlayerDomain，结算后马上更新缓冲和数据库
   * 
   * @param seat 座位信息.
   */
  private void doExitTabl(SharkSeat seat) {
    Player player = playerMgr.getPlayer(seat.getPlayerId());
    long playerId = player.getId();
    SharkTable table = seat.getTable();

    if (table.getStatus() == SharkTableStatus.GAMING) { // 游戏中，谁退就结算结算谁
      doBill(seat);
    } else if (table.getStatus() == SharkTableStatus.BET) { // 下注退,退还下注金币
      playerMgr.addGold(playerId,
          convertGold(seat.getTable().getRoom().getRoomDomain(), seat.totalBets()),
          LogReason.SHARK_EXIT_TABLE_RETURN_BET);
    }

    if (seat == table.getBanker()) { // 庄家退
      table.randSysBankerInfo();
      table.setBanker(table.getSysSeat());
      if (table.getStatus() == SharkTableStatus.REST) {
        doChangeBanker(table);
      }
    } else {
      table.getApplyBankers().remove(seat.getPlayerId());
      msgMgr.sendApplyBankersMsg(table);
    }

    // 发送退出房间消息
    msgMgr.sendExitTableMsg(seat);
    dataMgr.removePlayerSeat(seat.getPlayerId());
    LOG.info("[飞禽走兽]玩家[{}][{}]退出桌子[{}]", seat.getPlayerId(), seat.getPlayerName(),
        seat.getTable().getId());

    if (seat.getTable().getStatus() == SharkTableStatus.BET) { // 下注阶段退同步一次筹码
      msgMgr.sendTableBetInfosMsg(table);
    }

    // 清空座位信息
    seat.clear();
    player.curTable = null;
    player.curSeat = null;

    // 触发退出桌子事件
    eventMgr.post(new ExitTableEvent(player, seat));
  }

  /**
   * 玩家下注 .
   * 
   * @param player 玩家.
   * @param icon .
   * @param chips .
   */
  public ResMessage bet(Player player, int icon, long chips) {
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (icon < 1 || icon > 12) {
      LOG.error("[飞禽走兽]玩家[{}][{}]下注图标[{}]不合法", player.getPlayerName(), player.getId(), icon);
      HuohuaAssert.isTrue(false);
    }

    if (chips <= 0) {
      LOG.error("[飞禽走兽]玩家[{}][{}]下注金额[{}]不合法", player.getPlayerName(), player.getId(), chips);
      HuohuaAssert.isTrue(false);
    }

    if (seat == null) {
      LOG.error("[飞禽走兽]玩家[{}][{}]不在牌桌中不能下注", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }
    SharkTable table = seat.getTable();
    if (table.getStatus() != SharkTableStatus.BET) {
      LOG.error("[飞禽走兽]玩家[{}][{}]非下注阶段不能下注", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_BET_TIME_OVER);
    }

    SharkSeat banker = table.getBanker();
    if (seat == banker) {
      LOG.error("[飞禽走兽]玩家[{}][{}]是庄家不能下注", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    // Map<Integer, Long> bets = seat.bets;
    // long iconBet = bets.getOrDefault(icon, 0L) + chips;
    // if (iconBet > PLAYER_MAX_BET_CHIPS) {
    // LOG.error("[飞禽走兽]玩家[{}][{}]单个筹码下注不能超过[{}]", player.getPlayerName(), player.getId(),
    // PLAYER_MAX_BET_CHIPS);
    // HuohuaAssert.isTrue(false, ErrorCode.GAME_ICON_BET_MAX, "" + PLAYER_MAX_BET_CHIPS / 10000);
    // }

    if (chips > seat.getTotalChips()) {
      LOG.error("[飞禽走兽]玩家[{}][{}]下注[{}]不能超过自己的筹码[{}]", player.getPlayerName(), player.getId(),
          chips, seat.getTotalChips());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_CHIP_NOT_ENOUGH);
    }

    // 校验图标是否合法
    HuohuaAssert.isTrue(FEI_QINGS.contains(icon) || ZOU_SHOUS.contains(icon) || icon == FEI_QIN
        || icon == ZOU_SHOU || SHARK == icon);

    /*
     * 庄家是玩家时，校验庄家筹码是否足够 .
     */
    // 牌桌下注
    Map<Integer, Long> tableBets = table.getTableBets();
    // if (banker.playerId > 0) {
    Map<Integer, Long> tableBetsCopy = new HashMap<Integer, Long>();
    for (Integer key : tableBets.keySet()) {
      tableBetsCopy.put(key, tableBets.get(key));
    }
    Long hadBets = tableBetsCopy.getOrDefault(icon, 0L);
    tableBetsCopy.put(icon, hadBets + chips);

    long reward = calIconRewardChips(TONG_PEI, table.getIconRates(), tableBetsCopy);
    HuohuaAssert.isTrue(reward <= banker.getTotalChips(), ErrorCode.GAME_BANKER_CHIP_NOT_ENOUGH);
    // }

    Map<Integer, Long> bets = seat.getBets();
    long iconBet = bets.getOrDefault(icon, 0L) + chips;
    bets.put(icon, iconBet);
    long tableBet = tableBets.getOrDefault(icon, 0L);
    tableBets.put(icon, tableBet + chips);

    /*
     * 扣除筹码和金币 .
     */
    seat.setTotalChips(seat.getTotalChips() - chips);
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);
    playerMgr.minusGold(player, convertGold(seat.getTable().getRoom().getRoomDomain(), chips),
        LogReason.SHARK_BET);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));
    // 同步桌子下注筹码消息
    msgMgr.sendTableBetInfosMsg(seat.getTable());
    LOG.info("[飞禽走兽]玩家[{}][{}]下注图标[{}][{}]成功", player.getPlayerName(), player.getId(), icon, chips);
    // 发送下注成功
    return msgMgr.sendBetMsg(icon, iconBet);
  }

  /**
   * 续压.
   * 
   * @Title continueBet.
   * @author houdongsheng
   * @date 2018年1月13日 下午7:27:21
   * @param player 玩家
   * @return ResMessage
   */
  public ResMessage continueBet(Player player) {
    SharkSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[飞禽走兽]玩家[{}][{}]不在牌桌中不能下注", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }
    SharkTable table = seat.getTable();
    if (table.getStatus() != SharkTableStatus.BET) {
      LOG.error("[飞禽走兽]玩家[{}][{}]非下注阶段不能下注", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_BET_TIME_OVER);
    }
    // 判断筹码是否足够（计算上一场下注总金额）
    Map<Integer, Long> preBets = seat.getPreBets();
    int billBet = 0;
    for (Integer icon : preBets.keySet()) {
      billBet += preBets.get(icon);
    }
    IdeaAssert.isTrue(seat.getTotalChips() >= billBet, ErrorCode.GAME_CHIP_NOT_ENOUGH);

    // 桌子总下注
    Map<Integer, Long> tableBets = table.getTableBets();

    // if (table.banker.playerId > 0) { // 判断筹码是否足够
    // 复制一份下注数据
    Map<Integer, Long> tableBetsCopy = new HashMap<Integer, Long>();
    for (Integer key : tableBets.keySet()) {
      tableBetsCopy.put(key, tableBets.get(key));
    }
    // 用复制的那份数据，增加上场下注数据,判断庄家是否金币足够
    for (Integer icon : preBets.keySet()) {
      Long betItem = preBets.getOrDefault(icon, 0L);
      Long tableBetItem = tableBetsCopy.getOrDefault(icon, 0L);
      tableBetsCopy.put(icon, betItem + tableBetItem);
    }
    long reward = calIconRewardChips(TONG_PEI, table.getIconRates(), tableBetsCopy);
    HuohuaAssert.isTrue(reward <= table.getBanker().getTotalChips(),
        ErrorCode.GAME_BANKER_CHIP_NOT_ENOUGH);
    // }

    // 执行真正的下注
    for (Integer icon : preBets.keySet()) {
      Long iconBet = preBets.get(icon);
      seat.getBets().put(icon, seat.getBets().getOrDefault(icon, 0L) + iconBet); // 增加座位下注
      tableBets.put(icon, tableBets.getOrDefault(icon, 0L) + iconBet); // 增加table下注

      LOG.info("[飞禽走兽]玩家[{}][{}]下注图标[{}][{}]成功", player.getPlayerName(), player.getId(), icon,
          iconBet);
    }
    seat.setTotalChips(seat.getTotalChips() - billBet); // 扣除用户筹码

    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);
    playerMgr.minusGold(player, convertGold(seat.getTable().getRoom().getRoomDomain(), billBet),
        LogReason.SHARK_BET);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));
    // 同步桌子下注筹码消息
    msgMgr.sendTableBetInfosMsg(seat.getTable());

    return ResMessage.DEFAULT;
  }

  /**
   * 清除玩家,服务器出现bug，玩家卡在游戏中，后台可以清除玩家 .
   * 
   * @param playerId 玩家id.
   */
  private void doClearGameData(long playerId) {
    try {
      dataMgr.removePlayerSeat(playerId);
      /*
       * 清除玩家房间数据 .
       */
      for (SharkRoom room : dataMgr.allRooms()) {
        room.getPlayers().remove(playerId);
        /*
         * 清除房间中玩家所在桌子数据 .
         */
        for (SharkTable table : room.getTables().values()) {
          table.getApplyBankers().remove(playerId);
          if (table.getBanker().getPlayerId() == playerId) {
            table.randSysBankerInfo();
            table.setBanker(table.getSysSeat());
          }
          for (SharkSeat seat : table.getSeats()) {
            if (seat.getPlayerId() == playerId) {
              seat.clear();
              // Player player = playerMgr.getPlayer(playerId);
              // if (player != null) {
              // loginMgr.noticeLogoutForGameExit(player);
              // }
            }
          }
        }
      }

      Player player = playerMgr.getPlayer(playerId);
      if (player != null) {
        player.curRoom = null;
        player.curTable = null;
        player.curSeat = null;
        loginMgr.noticeLogoutForGameExit(player);
      }
    } catch (Exception e) {
      LOG.error("[飞禽走兽]清除玩家[" + playerId + "]卡限失败", e);
    }
  }
}
